﻿using System;
using System.Diagnostics;
using System.IO;
using System.Management;

namespace ListenerService
{
    public class UsbDriverWatcher
    {
        private ManagementEventWatcher addEventWatcher;
        private ManagementEventWatcher delEventWatcher;

        public void Start()
        {
            try
            {
                //DriveType
                //Value    Meaning
                //0        Unknown
                //1        No Root Directory
                //2        Removable Disk
                //3        Local Disk
                //4        Network Drive
                //5        Compact Disc
                //6        RAM Disk
                WqlEventQuery addQuery = new WqlEventQuery(EventClassName.__InstanceCreationEvent, new TimeSpan(0, 0, 0, 0, 500), @"TargetInstance isa 'Win32_LogicalDisk'");
                WqlEventQuery delQuery = new WqlEventQuery(EventClassName.__InstanceDeletionEvent, new TimeSpan(0, 0, 0, 0, 500), @"TargetInstance isa 'Win32_LogicalDisk'");

                ConnectionOptions opt = new ConnectionOptions();
                opt.EnablePrivileges = true;
                opt.Authority = null;
                opt.Authentication = AuthenticationLevel.Default;

                ManagementScope scope = new ManagementScope("\\root\\CIMV2", opt);

                addEventWatcher = new ManagementEventWatcher(scope, addQuery);
                addEventWatcher.EventArrived += eventWatcher_EventArrived;
                addEventWatcher.Start();

                delEventWatcher = new ManagementEventWatcher(scope, delQuery);
                delEventWatcher.EventArrived += eventWatcher_EventArrived;
                delEventWatcher.Start();
            }
            catch (Exception ex)
            {
                Trace.TraceError(ex.Message);
                Trace.TraceError(ex.StackTrace);
            }
        }

        public void Stop()
        {
            addEventWatcher?.Stop();
            delEventWatcher?.Stop();
        }

        private void eventWatcher_EventArrived(object sender, EventArrivedEventArgs e)
        {
            Trace.TraceInformation(e.NewEvent.GetText(TextFormat.Mof));
            ManagementBaseObject wmiDevice = (ManagementBaseObject)e.NewEvent["TargetInstance"];
            string driveName = (string)wmiDevice["DeviceID"];
            if (!string.IsNullOrEmpty(driveName))
            {
                DeviceChangedEventArgs eventArgs = new DeviceChangedEventArgs();
                eventArgs.DriverName = driveName;
                eventArgs.Description = (string)wmiDevice["Description"];
                eventArgs.Size = (UInt64)wmiDevice["Size"];
                eventArgs.FreeSpace = (UInt64)wmiDevice["FreeSpace"];
                if (e.NewEvent.ClassPath.ClassName == EventClassName.__InstanceCreationEvent)
                    eventArgs.Action = DeviceChangedAction.Add;
                else
                    eventArgs.Action = DeviceChangedAction.Remove;
                OnDeviceChanged?.Invoke(this, eventArgs);
            }
        }

        public event DeviceChangedEventHandler OnDeviceChanged;
    }

    public delegate void DeviceChangedEventHandler(object sender, DeviceChangedEventArgs e);

    public enum DeviceChangedAction
    {
        Add,
        Remove,
    }

    public class DeviceChangedEventArgs : EventArgs
    {
        public DeviceChangedAction Action { get; set; }
        public string FullPath { get { return Path.GetFullPath(DriverName); } }
        public string DriverName { get; set; }
        public string Description { get; set; }
        public UInt64 FreeSpace { get; set; }
        public UInt64 Size { get; set; }
    }

    public static class EventClassName
    {
        public static readonly string __AbsoluteTimerInstruction = "__AbsoluteTimerInstruction"; //Causes an event to be generated on a specific date at a specific time.
        public static readonly string __ACE = "__ACE"; //Represents an access control entry(ACE).
        public static readonly string __AggregateEvent = "__AggregateEvent"; //Represents an aggregate event of several individual intrinsic or extrinsic events.
        public static readonly string __ArbitratorConfiguration = "__ArbitratorConfiguration"; //Configuration class that limits the internal resources that are used by operations initiated by WMI clients.
        public static readonly string __CacheControl = "__CacheControl"; //Determines when WMI should release a Component Object Model (COM) object.
        public static readonly string __CIMOMIdentification = "__CIMOMIdentification"; //Describes the local installation of WMI.
        public static readonly string __ClassCreationEvent = "__ClassCreationEvent"; //Represents a class creation event, which is a type of intrinsic event generated when a new class is added to the namespace.
        public static readonly string __ClassDeletionEvent = "__ClassDeletionEvent"; //Represents a class deletion event, which is a type of intrinsic event generated when a class is removed from the namespace.
        public static readonly string __ClassModificationEvent = "__ClassModificationEvent"; //Represents a class modification event, which is a type of intrinsic event generated when a class is changed in the namespace.
        public static readonly string __ClassOperationEvent = "__ClassOperationEvent"; //A base class for all intrinsic events that relate to a class.
        public static readonly string __ClassProviderRegistration = "__ClassProviderRegistration"; //Registers class providers in WMI.
        public static readonly string __ConsumerFailureEvent = "__ConsumerFailureEvent"; //Represents the occurrence of some other event that is being dropped because of the failure of an event consumer.
        public static readonly string __Event = "__Event"; //An abstract base class that serves as the parent class for all intrinsic and extrinsic events.
        public static readonly string __EventConsumer = "__EventConsumer"; //An abstract base class that is used in the registration of a permanent event consumer.
        public static readonly string __EventConsumerProviderCacheControl = "__EventConsumerProviderCacheControl"; //Determines when WMI should release an event consumer provider.
        public static readonly string __EventConsumerProviderRegistration = "__EventConsumerProviderRegistration"; //Registers event consumer providers with WMI.
        public static readonly string __EventDroppedEvent = "__EventDroppedEvent"; //Represents the occurrence of an event that is dropped.A dropped event is an event that is not delivered to an event consumer.
        public static readonly string __EventFilter = "__EventFilter"; //Registration of a permanent event consumer requires an instance of the = "__EventFilter system class.
        public static readonly string __EventGenerator = "__EventGenerator"; //Serves as a parent class for classes that control the generation of events, such as timer events.
        public static readonly string __EventProviderCacheControl = "__EventProviderCacheControl"; //Controls when an event provider is unloaded.
        public static readonly string __EventProviderRegistration = "__EventProviderRegistration"; //Registers event providers with WMI.
        public static readonly string __EventQueueOverflowEvent = "__EventQueueOverflowEvent"; //Reports when an event is dropped as a result of delivery queue overflow.
        public static readonly string __EventSinkCacheControl = "__EventSinkCacheControl"; //Used to determine when WMI releases an event consumer provider's IWbemUnboundObjectSink pointer.
        public static readonly string __ExtendedStatus = "__ExtendedStatus"; //Used to report detailed status and error information.
        public static readonly string __ExtrinsicEvent = "__ExtrinsicEvent"; //Serves as a parent class for all user-defined event types, also known as extrinsic events.
        public static readonly string __FilterToConsumerBinding = "__FilterToConsumerBinding"; //Used in the registration of permanent event consumers to relate an instance of the of = "__EventConsumer to an instance of = "__EventFilter.
        public static readonly string __IndicationRelated = "__IndicationRelated"; //Serves as a parent class for all event-related classes.
        public static readonly string __InstanceCreationEvent = "__InstanceCreationEvent"; //Reports an instance creation event, which is a type of intrinsic event that is generated when a new instance is added to the namespace.
        public static readonly string __InstanceDeletionEvent = "__InstanceDeletionEvent"; //Reports an instance deletion event, which is a type of intrinsic event generated when an instance is deleted from the namespace.
        public static readonly string __InstanceModificationEvent = "__InstanceModificationEvent"; //Reports an instance modification event, which is a type of intrinsic event generated when an instance changes in the namespace.
        public static readonly string __InstanceOperationEvent = "__InstanceOperationEvent"; //Serves as a base class for all intrinsic events that relate to an instance.
        public static readonly string __InstanceProviderRegistration = "__InstanceProviderRegistration"; //Registers instance providers in WMI.
        public static readonly string __IntervalTimerInstruction = "__IntervalTimerInstruction"; //Generates events at intervals, similar to a WM_TIMER message in Windows programming.
        public static readonly string __MethodInvocationEvent = "__MethodInvocationEvent"; //This class is not implemented.
        public static readonly string __MethodProviderRegistration = "__MethodProviderRegistration"; //Registers method providers with WMI.
        public static readonly string __Namespace = "__Namespace"; //Represents a WMI namespace.
        public static readonly string __NamespaceCreationEvent = "__NamespaceCreationEvent"; //Reports a namespace creation event, which is a type of intrinsic event generated when a new namespace is added to the current namespace.
        public static readonly string __NamespaceDeletionEvent = "__NamespaceDeletionEvent"; //Reports a namespace deletion event, which is a type of intrinsic event that is generated when a sub-namespace is removed from the current namespace.
        public static readonly string __NamespaceModificationEvent = "__NamespaceModificationEvent"; //Reports a namespace modification event, which is a type of intrinsic event that is generated when a namespace is modified.
        public static readonly string __NamespaceOperationEvent = "__NamespaceOperationEvent"; //A base class for all intrinsic events that relate to a namespace.
        public static readonly string __NotifyStatus = "__NotifyStatus"; //Serves as the parent class for provider-defined error classes.
        public static readonly string __NTLMUser9X = "__NTLMUser9X"; //Controls remote access to a computer running unsupported versions of Windows.
        public static readonly string __ObjectProviderCacheControl = "__ObjectProviderCacheControl"; //Controls when a class or instance provider is unloaded.
        public static readonly string __ObjectProviderRegistration = "__ObjectProviderRegistration"; //Serves as the parent for classes that are used to register class and instance providers in WMI.
        public static readonly string __PARAMETERS = "__PARAMETERS"; //Defines the input and output parameters for methods.
        public static readonly string __PropertyProviderCacheControl = "__PropertyProviderCacheControl"; //Controls the cache when a property provider is unloaded.
        public static readonly string __PropertyProviderRegistration = "__PropertyProviderRegistration"; //Registers property providers in WMI.
        public static readonly string __Provider = "__Provider"; //Serves as the parent class for the = "__Win32Provider system class.
        public static readonly string __ProviderHostQuotaConfiguration = "__ProviderHostQuotaConfiguration"; //Allows limits to be set on host process usage of system resources.
        public static readonly string __ProviderRegistration = "__ProviderRegistration"; //Serves as the parent class for registration classes for various types of providers.
        public static readonly string __SecurityDescriptor = "__SecurityDescriptor"; //Represents a security descriptor.
        public static readonly string __SecurityRelatedClass = "__SecurityRelatedClass"; //Serves as a parent class for all types of security classes.
        public static readonly string __SystemClass = "__SystemClass"; //Base class from which most system classes derive.
        public static readonly string __SystemEvent = "__SystemEvent"; //Represents a system event.
        public static readonly string __SystemSecurity = "__SystemSecurity"; //Contains methods that let you access and modify the security settings for a namespace.
        public static readonly string __thisNAMESPACE = "__thisNAMESPACE"; //Holds the security rights for the namespace in the form of a security descriptor.
        public static readonly string __TimerEvent = "__TimerEvent"; //Reports an event generated by WMI in response to a consumer's request for an interval timer event or an absolute timer event.
        public static readonly string __TimerInstruction = "__TimerInstruction"; //Specifies instructions on how timer events should be generated for consumers.
        public static readonly string __TimerNextFiring = "__TimerNextFiring"; //Reserved for operating system use.
        public static readonly string __Trustee = "__Trustee"; //Represents a trustee. Either a name or a SID (byte array) can be used.
        public static readonly string __Win32Provider = "__Win32Provider"; //Registers information about a provider's physical implementation in WMI.
    }
}